package dd.lo.chapter12;

import dd.lo.util.ImageUtils;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.ByteArrayInputStream;
import java.io.File;

/**
 * 代码样例：Example 12-2. Using cv::HoughCircles() to return a sequence of circles found in a grayscale image
 * 参考资料：https://zhuanlan.zhihu.com/p/397325145
 */
public class Example2 extends Application {

    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        launch(args);
    }

    private static final int DEFAULT_WIDTH = 1080;
    private static final int DEFAULT_HEIGHT = 768;

    private enum Mode {
        ORIGIN,
        HOUGH_LINES,
        HOUGH_CIRCLES,
    }

    private Mat srcImg;

    private int modeIdx = 0;

    private Mode mode = Mode.ORIGIN;

    private ImageView imageView;

    private void computeConvolution() {
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() {
                if (null == srcImg) return null;
                Mat dst = new Mat();
                srcImg.copyTo(dst);
                if (mode == Mode.HOUGH_LINES) {
                    Mat temp = new Mat();
                    dst.copyTo(temp);
                    if (temp.channels() > 1) {
                        Imgproc.cvtColor(temp, temp, Imgproc.COLOR_BGR2GRAY);
                    }
                    Imgproc.Canny(temp, temp, 50, 150, 3);
                    Mat lines = new Mat();
                    Imgproc.HoughLinesP(temp, lines, 1, Math.PI / 180, 1, 110, 10);
                    for (int i = 0; i < lines.rows(); ++i) {
                        double[] vector = lines.get(i, 0);
//                        System.out.printf("%s\n", Arrays.stream(vector).mapToObj(d -> d + "").collect(Collectors.joining(",")));
                        Imgproc.line(dst, new Point(vector[0], vector[1]), new Point(vector[2], vector[3]), new Scalar(new double[]{0, 0, 255}), 5, Imgproc.LINE_8);
                    }
                } else if (mode == Mode.HOUGH_CIRCLES) {
                    Mat temp = new Mat();
                    dst.copyTo(temp);
                    if (temp.channels() > 1) {
                        Imgproc.cvtColor(temp, temp, Imgproc.COLOR_BGR2GRAY);
                    }
                    Imgproc.GaussianBlur(temp, temp, new Size(5, 5), 0 , 0);
                    Mat circles = new Mat();
                    Imgproc.HoughCircles(temp, circles, Imgproc.HOUGH_GRADIENT, 2, temp.cols() / 10.0);
                    for (int i = 0; i < circles.rows(); ++i) {
                        double[] vector = circles.get(i, 0);
//                        System.out.printf("%s\n", Arrays.stream(vector).mapToObj(d -> d + "").collect(Collectors.joining(",")));
                        Imgproc.circle(dst, new Point(vector[0], vector[1]), (int) vector[2], new Scalar(new double[]{0, 0, 255}), 5, Imgproc.LINE_8);
                    }
                }
                //调整图片大小以用于展示
                ImageUtils.fitImgSize(dst, DEFAULT_WIDTH, DEFAULT_HEIGHT);
                MatOfByte buffer = new MatOfByte();
                Imgcodecs.imencode(".jpeg", dst, buffer);
                Platform.runLater(() -> imageView.setImage(new Image(new ByteArrayInputStream(buffer.toArray()))));
                return null;
            }
        };
        new Thread(task).start();
    }

    @Override
    public void start(Stage stage) {
        //File chooser
        Button imageChooser = new Button("选择图片");
        imageChooser.setOnMouseClicked(e -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("请选择图片");
            fileChooser.setInitialDirectory(new File(System.getProperty("user.home") + "/Downloads")
            );
            fileChooser.getExtensionFilters().addAll(
//                    new FileChooser.ExtensionFilter("All Images", "*.*"),
                    new FileChooser.ExtensionFilter("JPEG", "*.jpeg"),
                    new FileChooser.ExtensionFilter("WEBP", "*.webp"),
                    new FileChooser.ExtensionFilter("PNG", "*.png"),
                    new FileChooser.ExtensionFilter("JPG", "*.jpg"),
                    new FileChooser.ExtensionFilter("GIF", "*.gif"),
                    new FileChooser.ExtensionFilter("BMP", "*.bmp")
            );
            File imgFile = fileChooser.showOpenDialog(stage);
            if (null == imgFile) {
                System.out.println("用户取消选中文件");
                return;
            }
            //读取一张图片
            srcImg = Imgcodecs.imread(imgFile.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
            if (srcImg.empty()) {
                throw new RuntimeException("图片读取失败");
            }
            computeConvolution();
        });
        //Mode indicator
        Text modeText = new Text("当前模式：" + mode);
        Button prevModeButton = new Button("上一个模式");
        prevModeButton.setOnMouseClicked(e -> {
            --modeIdx;
            modeIdx = (Mode.values().length + modeIdx) % Mode.values().length;
            mode = Mode.values()[modeIdx];
            modeText.setText("当前模式：" + mode);
            computeConvolution();
        });
        Button nextModeButton = new Button("下一个模式");
        nextModeButton.setOnMouseClicked(e -> {
            ++modeIdx;
            modeIdx = modeIdx % Mode.values().length;
            mode = Mode.values()[modeIdx];
            modeText.setText("当前模式：" + mode);
            computeConvolution();
        });
        //Exit button
        Button exitButton = new Button("关闭");
        exitButton.setOnMouseClicked(e -> {
            Platform.exit();
        });
        FlowPane bottomControl = new FlowPane();
        bottomControl.getChildren().addAll(imageChooser, prevModeButton, modeText, nextModeButton, exitButton);
        bottomControl.setPrefHeight(50);
        BorderPane root = new BorderPane();
        imageView = new ImageView();
        root.setCenter(imageView);
        root.setBottom(bottomControl);
        Scene scene = new Scene(root, DEFAULT_WIDTH, DEFAULT_HEIGHT + 50);
        stage.setScene(scene);
        stage.setTitle("Example2 - Hough lines, hough circles");
        stage.setResizable(false);
        stage.show();
    }
}
